/*
 * Network.h
 *
 *  Created on: Nov 23, 2012
 *      Author: dan
 */

#ifndef NETWORK_H_
#define NETWORK_H_

#include <boost/thread.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <map>
#include <set>
#include <algorithm>

#include "Print.hpp"

namespace {

typedef boost::asio::ip::tcp::socket socket_t;
typedef boost::shared_ptr<socket_t> socket_ref;
typedef std::map<std::string, socket_ref> Hosts;
typedef boost::system::error_code error_code;
typedef boost::thread thread_t;
typedef boost::shared_ptr<thread_t> thread_ref;
typedef std::map<std::string, thread_ref> Threads;
typedef boost::asio::io_service ios_t;
typedef boost::shared_ptr<ios_t> ios_ref;

}


class Network {
public:

	struct Address{
		std::string host;
		std::string port;
		Address(std::string host, std::string port):host(host),port(port){}
		Address(std::string host, int int_port):host(host){
			std::stringstream s; s<<int_port; port=s.str();
		}
		int getPort()const{
			std::stringstream s(port); int p; s>>p; return p;
		}
		std::string str()const{ return host+":"+port; }
		operator std::string()const{ return str(); }
		operator const char*()const{ return str().c_str();}
 	};
	class Team{
	public:
		typedef std::vector<Address> HostsList;
		typedef HostsList::iterator iterator;
		typedef HostsList::const_iterator const_iterator;
		HostsList hosts;
		Team(const HostsList& h):hosts(h){
		}
		Team(){
		}
		Team(const Team& t):hosts(t.hosts){
		}
		void insert(const Address& address){
			hosts.push_back(address);
		}
		void add(const Address& address){insert(address);}
		void push_back(const Address& address){insert(address);}
		iterator begin(){ return hosts.begin(); }
		iterator end(){ return hosts.end(); }
		const_iterator begin()const{ return hosts.begin(); }
		const_iterator end()const{ return hosts.end(); }
		void erase(iterator i){ hosts.erase(i); }
		void clear(){ hosts.clear(); }
		size_t size()const{ return hosts.size(); }
		iterator find(const Address& address){
			for(iterator i=begin(); i!=end(); i++){
				if( i->str() == address.str() ) return i;
			}
			return end();
		}
		Address& operator[](size_t i){ return hosts[i]; }
		Address operator[](size_t i)const{ return hosts[i]; }
		operator std::vector<Address>(){ return hosts; }
		operator std::set<std::string>(){
			std::set<std::string> res;
			for(iterator i=begin(); i!=end(); i++){
				res.insert(i->str());
			}
			return res;
		}
		operator std::vector<std::string>(){
			std::vector<std::string> res;
			for(iterator i=begin(); i!=end(); i++){
				res.push_back(i->str());
			}
			return res;
		}
	};

	Network(const Address& addr);

	void runServer();
	void connectTo(std::string host, std::string id, boost::system::error_code& error);
	void connectToTeam(const Team& team);
	void connect(const Team& team);

	int send(const std::string& id, std::string message, error_code& error);
	void send(const Team& ids, std::string message);
	void send(const std::vector<std::string>& ids, std::string message);
	void send(const std::set<std::string>& ids, std::string message);
	void broadcast(std::string message);

	std::string read(const std::string& id, error_code& error);


	std::vector<std::string> getConnections();
	std::set<std::string> getConnectionsSet();
	bool isMe(std::string _id)const;
	bool MeLessThen(std::string _id)const;

private:

	void addHost(const std::string& id, socket_ref socket);

	void removeHost(const std::string& id);
	bool threadStop(const std::string& id);

	void reading(std::string id);

//---------------- EVENTS ----------------------
public:

	typedef boost::function < void (std::string, error_code) >  onConnect_t;
	typedef boost::function < void (std::string, error_code) >  onDisconnect_t;
	typedef boost::function < void (std::string, std::string, error_code) >  onMessage_t;

	size_t setCallbacks(onConnect_t _onConnect, onDisconnect_t _onDisconnect, onMessage_t _onMessage);
	void removeCallbacks(size_t setid);

private:
	std::map<size_t, onConnect_t> onConnect;
	std::map<size_t, onDisconnect_t> onDisconnect;
	std::map<size_t, onMessage_t> onMessage;

	inline void fireOnConnect(std::string id, error_code error){
		for(std::map<size_t, onConnect_t>::iterator i=onConnect.begin();i!=onConnect.end();i++)
			(i->second)(id, error);
	}
	inline void fireOnDisconnect(std::string id, error_code error){
		for(std::map<size_t, onDisconnect_t>::iterator i=onDisconnect.begin();i!=onDisconnect.end();i++)
			(i->second)(id, error);
	}
	inline void fireOnMessage(std::string id, std::string message, error_code error){
		for(std::map<size_t, onMessage_t>::iterator i=onMessage.begin();i!=onMessage.end();i++)
			(i->second)(id, message, error);
	}


private:
	boost::asio::io_service io_service;
	std::string id;
	std::string teamid;
	Hosts hosts;
	Threads threads;
	bool stop_server;
	boost::mutex host_mtx;

public:
	DebugPrint debug_print;
};


#endif /* NETWORK_H_ */
